home *** CD-ROM | disk | FTP | other *** search
/ Amiga Developer CD 2.1 / Amiga Developer CD v2.1.iso / Reference / Amiga_Mail_Vol2 / Archives / Plain / nd92 / R2Packets / R2Packets.txt < prev   
Encoding:
Text File  |  1992-12-18  |  19.2 KB  |  436 lines

  1. (c)  Copyright 1992 Commodore-Amiga, Inc.   All rights reserved.
  2. The information contained herein is subject to change without notice,
  3. and is provided "as is" without warranty of any kind, either expressed
  4. or implied.  The entire risk as to the use of this information is
  5. assumed by the user.
  6.  
  7. Packet Level I/O under Release 2
  8.  
  9.  
  10. by Dale Larson and John Orr
  11.  
  12.  
  13. Normally when an application needs to perform I/O using DOS, it uses
  14. functions from the dos.library to open, read, write, and close any
  15. I/O channels.  These functions take care of talking to the underlying
  16. DOS device (see the ``About Devices'' section below), shielding the
  17. programmer from most of the grunt work it takes to carry out the I/O.
  18. These functions are adequate for most I/O needs.
  19.  
  20.  *********************************************************
  21.  *                                                       *
  22.  * About Devices                                         *
  23.  *                                                       *
  24.  *                                                       *
  25.  * The term ``DOS devices'' is confusing because the     *
  26.  * term ``device'' is a bit overloaded.  There are       *
  27.  * three entities on the Amiga that are referred to as   *
  28.  * a device.  There is the Exec device (for example      *
  29.  * trackdisk.device and console.device) the AmigaDOS     *
  30.  * device (such as DF0: and RAM:), and the logical       *
  31.  * AmigaDOS device name (for example, SYS:, C:, and      *
  32.  * S:).  The Exec level device is basically a subset of  *
  33.  * an Exec library.  Exec devices are described in the   *
  34.  * RKRM: Devices book. The AmigaDOS device is a higher   *
  35.  * level entity than the Exec device (AmigaDOS devices   *
  36.  * often utilize Exec devices).  An AmigaDOS device has  *
  37.  * a process associated with it, normally referred to    *
  38.  * as a handler, that controls the AmigaDOS I/O stream   *
  39.  * via a FileHandle.  The AmigaDOS logical device name   *
  40.  * (better known as an ``assign'') is made to look like  *
  41.  * an AmigaDOS device, but is a higher level entity      *
  42.  * than the AmigaDOS device.  Both the AmigaDOS device   *
  43.  * and the logical device name are referred to by a      *
  44.  * name ending in a colon (':').  The logical device     *
  45.  * can refer to any directory on an AmigaDOS device (as  *
  46.  * long as the AmigaDOS device supports having files).   *
  47.  * The user can change the directory to which logical    *
  48.  * device name refers from a command line. This article  *
  49.  * deals primarily with AmigaDOS devices.                *
  50.  *                                                       *
  51.  *********************************************************
  52.  
  53. One of the reasons these functions are only adequate is because they
  54. are synchronous.  When an application attempts to transfer data using
  55. one of these functions, the I/O function waits for the entire
  56. transfer to finish before returning.  Ideally, the application should
  57. be able to perform I/O asynchronously, so it can do something else
  58. while the data transfer takes place.
  59.  
  60. Another reason that these functions are only adequate is because they
  61. don't implement all of the features built into DOS devices.  To
  62. utilize these features, an application has to work on a lower level.
  63. The application has to talk directly to the DOS device.
  64.  
  65.  
  66. The Packet Level
  67.  
  68. When DOS functions talk to devices such as the floppy drive or the
  69. serial port, they talk to a special process called a handler.  Every
  70. DOS device (like CON:, SER:, DF0:, and PIPE:) has a handler process.
  71. The handler process is responsible for receiving and carrying out a
  72. standard set of DOS device commands.  It provides a standard
  73. programming interface to a lower-level I/O software or hardware
  74. entity (such as an Exec device).  The packet interface abstracts the
  75. lower-level entity so that dos.library functions don't have to deal
  76. with a lot of bothersome details such as moving a disk head or
  77. reading serial registers.   The interface to every handler is the
  78. same, so every DOS device operates in the same manner, regardless of
  79. any underlying software or hardware.  Theoretically, to the
  80. dos.library functions, writing to the console handler (CON:) should
  81. be no different than writing to the serial port handler (SER:) or the
  82. DF0: handler.  The handler lets dos.library functions treat all DOS
  83. devices in the same way.
  84.  
  85. Typically, the handler talks directly to an underlying Exec device.
  86. Two examples are the CON: and the DF0: handlers.  The CON: handler
  87. process talks directly to the console.device.   When trying to access
  88. a floppy in DF0:, the DF0: handler talks directly to the
  89. trackdisk.device.  These handlers accept handler level commands (for
  90. example, ACTION_READ and ACTION_WRITE) and hide any Exec level I/O
  91. from the dos.library functions and subsequently, the application.
  92.  
  93. In cases like the DF0: handler, which is a special type of handler
  94. called a file system, the handler has to take care of organizing the
  95. lower-level medium into directories and files.  The DF0: handler
  96. takes care of translating directory and file names into terms the
  97. trackdisk.device can understand (disk blocks).  The only requirement
  98. of a handler to be a file system is that the handler must support
  99. files.  A file system does not have to support a directory structure
  100. to be considered a file system.  Handlers which are not file systems
  101. are called stream handlers.
  102.  
  103. Some handlers do not have any underlying software or hardware.
  104. Handlers such as RAM: have no underlying software or hardware.  These
  105. kinds of handlers take care of implementing everything necessary to
  106. carry out the I/O rather than delegating to an Exec device.
  107.  
  108. Handlers receive commands through an Exec message port.  Every
  109. handler process has a message port for receiving commands.  A handler
  110. accepts a command in the form of a DosPacket structure (defined in
  111. <dos/dosextens.h>), which is an extension of an Exec Message
  112. structure:
  113.  
  114. struct DosPacket {
  115.    struct Message *dp_Link;
  116.    struct MsgPort *dp_Port;  /* Reply port for the packet */
  117.                              /* Must be filled in each send. */
  118.    LONG dp_Type;
  119.    LONG dp_Res1;             /* Result #1 */
  120.    LONG dp_Res2;             /* For file system calls this is what would
  121.                               * have been returned by IoErr() */
  122.    LONG dp_Arg1;             /* Argument list */
  123.    LONG dp_Arg2;
  124.    LONG dp_Arg3;
  125.    LONG dp_Arg4;
  126.    LONG dp_Arg5;
  127.    LONG dp_Arg6;
  128.    LONG dp_Arg7;
  129. }; /* DosPacket */
  130.  
  131. The dp_Type field contains an identifier corresponding to the
  132. command.  The identifiers for each of the standard commands are
  133. defined in <dos/dosextens.h>.  For example, the command to write data
  134. is ACTION_WRITE.  Each packet type has different parameters, which
  135. the programmer supplies in the ``dp_Arg'' fields.
  136.  
  137. After a handler finishes with a packet, it returns the packet to the
  138. message port in dp_Port.  The handler places return values (including
  139. any error codes) in the result fields dp_Res1 and dp_Res2.  If there
  140. was an error, dp_Res2 contains the corresponding DOS error code.
  141.  
  142. The packet types are described in the Amiga Mail article ``AmigaDOS
  143. Packet Interface Specification'' on page II-5 and also in The
  144. AmigaDOS Manual, 3rd Edition.  Since its original publication, the
  145. ``AmigaDOS Packet Interface Specification'' has been updated numerous
  146. times in Amiga Mail to correct errors.  The information in that
  147. article (plus its errata) should appear in the next edition of The
  148. AmigaDOS Manual.
  149.  
  150.  
  151. Finding the Handler
  152.  
  153. There are various ways to find the address of the target handler's
  154. Message port, which is also called a process identifier by some
  155. documentation.  When working with an open FileHandle, the handler's
  156. port address is in the FileHandle's fh_Type field.  Note that the
  157. dos.library functions normally access a FileHandle using a BPTR, so
  158. to get to the fh_Type field an application has to do something like
  159. this:
  160.  
  161.     my_handler_port = ((struct FileHandle *)BADDR(FileHandle))->fh_Type);
  162.  
  163. Because the AmigaDOS device NIL: has no handler process, the fh_Type
  164. field of any of its file handle's will be NULL.  The application must
  165. account for this case.
  166.  
  167. When working with a device or assign name, an application can get to
  168. the handler's message port by using the dos.library function
  169. GetDeviceProc():
  170.  
  171.     struct DevProc *GetDeviceProc(UBYTE *dev_name, struct DevProc *prev_devproc);
  172.  
  173. This function returns a pointer to the following structure:
  174.  
  175. struct DevProc {
  176.         struct MsgPort *dvp_Port;     /* The handler's Message port     */
  177.         BPTR            dvp_Lock;     /*           (send packets there) */
  178.         ULONG           dvp_Flags;
  179.         struct DosList *dvp_DevNode;  /* DON'T TOUCH OR USE! */
  180. };
  181.  
  182. This function is intended to improve on the DeviceProc() function as
  183. it also deals with multiple assigns.  GetDeviceProc() must be
  184. countered by FreeDeviceProc().  See the GetDeviceProc() Autodoc for
  185. more details.
  186.  
  187.  
  188. The Old Way
  189.  
  190. Prior to Release 2, sending packets was relatively complicated.  Some
  191. of the early disks from the Fred Fish Library (disks 35, 56, and 66)
  192. contain complete examples of using DOS packets synchronously and
  193. asynchronously.  As of Release 2.04, dos.library contains functions
  194. which make it easier to use DOS packets synchronously and
  195. asynchronously.
  196.  
  197.  
  198. Synchronous Packet Calls
  199.  
  200. Performing synchronous packet I/O is now very simple.  Simply call
  201. the dos.library function DoPkt():
  202.  
  203. LONG DoPkt(struct MsgPort *handler_port, LONG action_id, LONG arg1,
  204.            LONG arg2, LONG arg3, LONG arg4, LONG arg5);
  205.  
  206. This function allocates and fills out a DosPacket using the packet
  207. type ('action_id') and arguments ('arg1', 'arg2', etc.) you supply.
  208. This function then sends off the packet to the 'handler_port' and
  209. waits for the packet to return.   DoPkt() returns the value from the
  210. packet's dp_Res1 field.  To get the value from the packet's dp_Res2
  211. field, call the dos.library function IoErr().  Assembly language
  212. programmers can also get dp_Res2 from register D1.
  213.  
  214.  
  215. Asynchronous Packet Calls
  216.  
  217. To do asynchronous packet calls, things are a little more complex,
  218. but they are still much better than they were before V37 (This
  219. subject was partially covered in Martin Taillefer's article, ``Fast
  220. AmigaDOS I/O'', page II-77, from the September/October 1992 issue of
  221. Amiga Mail).  When using a DOS packet asynchronously, there are three
  222. things to do before sending the packet anywhere:
  223.  
  224.     o Acquire a message port
  225.     o Allocate and initialize the packet
  226.     o Set up the packet's arguments
  227.  
  228.  
  229. Acquiring the Message Port
  230.  
  231. It is possible for an application to use its process message port for
  232. packet transmissions rather than allocating a new one.  It can get to
  233. its Process structure by calling FindTask() with an argument of NULL.
  234. The Process structure has an Exec MsgPort structure embedded in it,
  235. which an application can get to via the pr_MsgPort field.  The bad
  236. thing about using this port is that many system functions also use
  237. it.  Consequently, after sending an asynchronous packet, an
  238. application can not call any system functions that use pr_MsgPort.
  239. This includes most dos.library functions, many C compiler linker
  240. library functions, and many standard I/O functions (i.e., printf()
  241. and scanf()).  The application has to wait for the asynchronous
  242. packet to return before calling such functions.  If the application
  243. sent an asynchronous packet and inadvertently initiated some other
  244. packet level I/O before the asynchronous packet came back, it is
  245. possible to cause an ``unexpected packet received'' system crash if
  246. the asynchronous packet returned at the wrong time.  Also, an
  247. application should remove such a packet from the process message port
  248. using the WaitPkt() function.  This function will take care of any
  249. system idiosyncrasies that the application would otherwise need to
  250. address itself.  This applies to idiosyncrasies that exist now or new
  251. ones that may appear in the future.
  252.  
  253. As of V37, arguably the best and easiest way to acquire a message
  254. port is to create one with the exec.library call CreateMsgPort().  By
  255. allocating its own message port, the application doesn't have to
  256. worry about any problems with sharing the port.
  257.  
  258.  
  259. Allocating and Initializing a Packet
  260.  
  261. The packet I/O system uses Exec's message passing system, so each
  262. DosPacket must also have an Exec Message structure.  As both
  263. structures are necessary, they have been combined in a single
  264. StandardPacket structure (defined in <dos/dosextens.h>):
  265.  
  266. struct StandardPacket {
  267.    struct Message   sp_Msg;
  268.    struct DosPacket sp_Pkt;
  269. }; /* StandardPacket */
  270.  
  271. To make a packet usable, the Exec Message and DosPacket structures
  272. have to be set up to point to each other.  The DosPacket structure is
  273. straightforward about its link to its corresponding message.  The
  274. DosPacket's dp_Link field must point to the packet's Message
  275. structure.  However, the link from the Message to the DosPacket is a
  276. bit obscure.  The Message contains an Exec Node structure which in
  277. turn contains a field called ln_Name.  Although the Node structure
  278. defines ln_Name as a character array, the DOS packet I/O system
  279. instead requires that this field point to the Message's corresponding
  280. DosPacket:
  281.  
  282.     my_standard_pkt->sp_Msg.mn_Node.ln_Name = (STRPTR)(my_standard_pkt->sp_Pkt);
  283.  
  284. As of V37, the dos.library function AllocDosObject() is usually the
  285. best way to allocate a StandardPacket.  To allocate one, call:
  286.  
  287.     mypacket = AllocDosObject(DOS_STDPKT, NULL);
  288.  
  289. This function takes care of linking the StandardPacket's Message and
  290. DosPacket.  Note that the function call above does not return a
  291. pointer to a StandardPacket.  This function allocates an entire
  292. StandardPacket structure, but it returns a pointer to the
  293. StandardPacket's sp_Pkt field.  To access the sp_Msg portion of the
  294. StandardPacket, use the pointer in the dp_Link field.
  295.  
  296. Any structure allocated with AllocDosObject() must be freed with
  297. FreeDosObject().  To free 'mypacket' from the above AllocDosObject()
  298. call:
  299.  
  300.     FreeDosObject(DOS_STDPKT, mypacket);
  301.  
  302.  
  303. Filling in Packet Arguments
  304.  
  305. Before sending an asynchronous packet, an application needs to fill
  306. in its action and arguments.  For example, a packet set up to read
  307. 4096 bytes from an open file handle might look something like this:
  308.  
  309. . . .
  310.  
  311. #define BUFSIZE 4096
  312.  
  313. . . .
  314.  
  315. BPTR myfh;
  316. struct DosPacket *my_pkt;
  317. UBYTE *buffer[BUFSIZE];
  318.  
  319.         . . .
  320.  
  321.         my_pkt->dp_Type = ACTION_READ;
  322.         my_pkt->dp_Arg1 = ((struct FileHandle *)BADDR(myfh))->fh_Arg1;
  323.         my_pkt->dp_Arg2 = buffer;
  324.         my_pkt->dp_Arg3 = BUFSIZE;
  325.  
  326.         . . .
  327.  
  328.  
  329. Sending the Asynchronous Packet
  330.  
  331. To send a packet asynchronously, use the dos.library SendPkt() function:
  332.  
  333.     SendPkt(struct DosPacket *mypacket,
  334.             struct MsgPort *handlerport, struct MsgPort *replyport)
  335.  
  336. This function sends 'mypacket' to 'handlerport' and exits without
  337. waiting for the packet to come back.  When the packet returns, it
  338. will arrive at 'replyport'.
  339.  
  340.  
  341. Waiting for the Asynchronous Packet
  342.  
  343. After calling SendPkt(), the application can go about its business,
  344. taking care of some other work while the DOS device handles the
  345. application's packet.  Eventually, the application will have to test
  346. the reply port to see if the packet has come back yet.  It can do
  347. this with WaitPort() or, if the application has to test for more than
  348. just the reply port's signal, it can use Wait().  If the application
  349. doesn't poll its signals too often, it can test its own signal bits
  350. using SetSignal() (see the exec.library Autodocs and the
  351. ``Introduction to Exec'' chapter of the Release 2 RKRM: Devices for
  352. more information on how to use these functions).  Be sure to remove
  353. any and all packets from the message port using GetMsg().
  354.  
  355. If the application used its process message port (pr_MsgPort) as the
  356. reply port, it must use the WaitPkt() function to remove the packet
  357. from the message port.  As mentioned earlier, this function will take
  358. care of any hidden system idiosyncrasies so the application never has
  359. to account for them.  WaitPkt() will not necessarily clear the
  360. process message port's signal bit.  Like SendPkt(), WaitPkt() assumes
  361. any DosPacket it works with is part of a StandardPacket structure.
  362.  
  363. The dos.library contains a function called AbortPkt() that, at a
  364. glance, looks like it might be useful to an application that needs to
  365. abort a packet (for example, upon receiving a break signal).  This
  366. function, which was introduced in Release 2, is supposed to attempt
  367. to abort a packet that has already been sent to a handler.  If the
  368. abort operation is successful, the aborted packet will arrive at its
  369. original reply port (the same place the packet would have arrived if
  370. it was successful).  Unfortunately, the abort operation will never be
  371. successful, at least not under existing releases of the operating
  372. system.  Currently, this function returns without doing anything (the
  373. most recent release is 3.0 or V39).  In the future when AbortPkt()
  374. does do something, it will assume that any DosPacket it works with is
  375. part of a StandardPacket structure.
  376.  
  377.  
  378. Interpreting the Packet Results
  379.  
  380. Upon returning to the application, for most packets, there will be
  381. result values in dp_Res1 and dp_Res2.  For most packets, if dp_Res1
  382. is DOSTRUE, the packet returned without a problem.  If dp_Res1 is
  383. DOSFALSE, the handler experienced an error with the packet and there
  384. should be an error code in dp_Res2.  The error codes are defined in
  385. <dos/dos.h>.  Note that not all packets follow this error reporting
  386. convention.  See the ``AmigaDOS Packet Interface Specification''
  387. article on page II-5 for more information on how individual packets
  388. work.
  389.  
  390.  
  391. Packets without dos.library Functions
  392.  
  393. Besides offering the ability to do asynchronous I/O, directly using
  394. DOS packets also allows applications to utilize features of certain
  395. handlers that are not available through a dos.library function.
  396.  
  397. The following packets are not available via a dos.library function as
  398. of Release 3.0:
  399.  
  400.     ACTION_WRITE_PROTECT    ACTION_DISK_INFO (for console handlers)
  401.  
  402. If an application needs the feature that these packets provide, the
  403. application has to send the packet to the handler.  The first two
  404. packets are fairly straightforward and are explained in the
  405. ``AmigaDOS Packet Interface Specification'' article on page II-5 of
  406. Amiga Mail.
  407.  
  408. The ACTION_DISK_INFO packet is peculiar because its function changes
  409. depending on the handler.  When sent to a file system handler, it
  410. returns information about its media.  The dos.library function Info()
  411. uses this packet.  The ACTION_DISK_INFO packet has a different
  412. meaning to a console handler.  When a console handler receives this
  413. packet, it returns a pointer to the window of the open console file
  414. handle.  When an application needs this pointer, the only way to get
  415. it as of Release 3.0 is to send the console handler this packet.
  416.  
  417. There are two commonly used packets that an application could not
  418. call through a dos.library function under 1.3 that now have
  419. corresponding dos.library functions.  The packet to rename a disk,
  420. ACTION_RENAME_DISK, is now available through the dos.library function
  421. Relabel().  Also the ACTION_SCREEN_MODE packet acquired a dos.library
  422. function, SetMode().
  423.  
  424.  
  425. About the Examples
  426.  
  427. Two examples accompany this article.  The first, CompareIO.c, uses
  428. packet level I/O to copy the standard input channel to the standard
  429. output channel (as set up by the standard startup code, c.o).
  430. CompareIO uses both synchronous and asynchronous I/O to perform the
  431. copy and reports the time it takes to do each.  The other example,
  432. InOutCTRL-C.c, also uses packets to copy the standard input channel
  433. to the standard output channel, but it only uses asynchronous I/O.
  434. The second example does a better job checking for a user break.
  435.  
  436.